package cn.echo.ecc.service.impl;

import cn.echo.ecc.constants.RedisConstants;
import cn.echo.ecc.domain.Course;
import cn.echo.ecc.domain.CourseMarket;
import cn.echo.ecc.domain.CourseOrder;
import cn.echo.ecc.domain.CourseOrderItem;
import cn.echo.ecc.dto.*;
import cn.echo.ecc.enums.GlobalExceptionCode;
import cn.echo.ecc.feign.CourseService;
import cn.echo.ecc.mapper.CourseOrderMapper;
import cn.echo.ecc.result.JSONResult;
import cn.echo.ecc.service.ICourseOrderItemService;
import cn.echo.ecc.service.ICourseOrderService;
import cn.echo.ecc.util.AssertUtil;
import cn.echo.ecc.util.CodeGenerateUtils;
import cn.echo.ecc.util.StrUtils;
import cn.echo.ecc.vo.CourseInfoVo;
import cn.echo.ecc.vo.CourseOrderVo;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.client.producer.TransactionSendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author hulan
 * @since 2022-10-15
 */
@Service
@Slf4j
public class CourseOrderServiceImpl extends ServiceImpl<CourseOrderMapper, CourseOrder> implements ICourseOrderService {

    @Autowired
    private RedisTemplate<Object,Object> redisTemplate;

    @Autowired
    private CourseService courseService;

    @Autowired
    private ICourseOrderItemService courseOrderItemService;

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    @Override
    public String placeOrder(CourseOrderDto courseOrderDto) {
        // 1.JSR303参数校验
        List<Long> courseIds = courseOrderDto.getCourseIds();
        Integer payType = courseOrderDto.getPayType();
        String token = courseOrderDto.getToken();
        // 2.校验token是否存在，是否正确
        String strIds = courseIds.stream().map(id -> String.valueOf(id)).collect(Collectors.joining(","));
        Long loginId = 4L;
        String key = String.format(RedisConstants.COURSE_ORDER_KEY, strIds, loginId);
        Object redisToken = redisTemplate.opsForValue().get(key);
        AssertUtil.isNotNull(redisToken, GlobalExceptionCode.ORDER_TOKEN_NON_EXISTENT_ERROR);
        AssertUtil.isEquals(token,redisToken.toString(),GlobalExceptionCode.ORDER_TOKEN__ERROR);
        // 3.根据id查询课程信息和总价
        JSONResult jsonResult = courseService.courseOrderInfo(strIds);
        AssertUtil.isTrue(jsonResult.isSuccess(),GlobalExceptionCode.SERVICE_ERROR);
        Object data = jsonResult.getData();
        AssertUtil.isNotNull(data,GlobalExceptionCode.SERVICE_ERROR);
        CourseOrderVo courseOrderVo = JSON.parseObject(JSON.toJSONString(data), CourseOrderVo.class);
        List<CourseInfoVo> courseInfos = courseOrderVo.getCourseInfos();
        AssertUtil.isNotNull(courseInfos,GlobalExceptionCode.COURSE_INFO_ERROR);
        BigDecimal totalAmount = courseOrderVo.getTotalAmount();
        // 4.保存课程订单信息t_course_order
        CourseOrder courseOrder = new CourseOrder();
        courseOrder.setCreateTime(new Date());
        courseOrder.setOrderNo(CodeGenerateUtils.generateOrderSn(loginId));
        courseOrder.setPayType(payType);
        courseOrder.setStatusOrder(CourseOrder.WAIT_PAY_STATUS);
        courseOrder.setUserId(loginId);
        courseOrder.setTotalAmount(totalAmount);
        courseOrder.setVersion(0);
        courseOrder.setTotalCount(0);
//        insert(courseOrder);
        String title = "购买课程 [";
        // 5.保存课程订单快照系信息t_course_order_item
        for (CourseInfoVo courseInfo : courseInfos) {
            Course course = courseInfo.getCourse();
            CourseMarket courseMarket = courseInfo.getCourseMarket();
            CourseOrderItem courseOrderItem = new CourseOrderItem();
            courseOrderItem.setAmount(courseMarket.getPrice());
            courseOrderItem.setCount(1);
            courseOrderItem.setCourseId(course.getId());
            courseOrderItem.setCreateTime(new Date());
            courseOrderItem.setCoursePic(course.getPic());
            courseOrderItem.setCourseName(course.getName());
//            courseOrderItem.setOrderId(courseOrder.getId());
            courseOrderItem.setOrderNo(courseOrder.getOrderNo());
            courseOrderItem.setVersion(0);
            courseOrder.getCourseOrderItems().add(courseOrderItem);
//            courseOrderItemService.insert(courseOrderItem);
            title = title + course.getName() + ";";
        }
        // 6.给课程订单信息设置title，更新到数据库
        title = title + "] , 支付[" + totalAmount + "] 元";
        courseOrder.setTitle(title);
//        updateById(courseOrder);
        // 7.封装消息message，发送事务消息
        PayDto payDto = new PayDto();
        BeanUtils.copyProperties(courseOrder,payDto);
        PayExtParamDto payExtParamDto = new PayExtParamDto();
        payExtParamDto.setCourseIds(strIds);
        payExtParamDto.setUserId(loginId);
        payExtParamDto.setOrderNo(courseOrder.getOrderNo());
        payDto.setExtParams(JSON.toJSONString(payExtParamDto));
        Message<PayDto> message = MessageBuilder.withPayload(payDto).build();
        TransactionSendResult sendResult = rocketMQTemplate.sendMessageInTransaction("tx-course-order-producer-group",
                "tx-course-order-topic:tx-course-order-tag",
                message,
                JSON.toJSONString(courseOrder));
        boolean sendState = sendResult.getSendStatus() == SendStatus.SEND_OK;
        AssertUtil.isTrue(sendState,GlobalExceptionCode.SEND_TX_MESSAGE_ERROR);
        // 8.删除防重Token
        redisTemplate.delete(key);
        try {
            // 9.发送延迟事务消息
            Message<String> delayMessage = MessageBuilder.withPayload(courseOrder.getOrderNo()).build();
            SendResult syncSendResult = rocketMQTemplate.syncSend("order-timeout-topic:order-timeout-tag",
                    delayMessage,
                    3000, 3);
            if (syncSendResult.getSendStatus() != SendStatus.SEND_OK) {
                log.info("课程订单发送延迟消息失败！");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return courseOrder.getOrderNo();
    }

    @Override
    public CourseOrder selectByOrderNo(String orderNo) {
        Wrapper<CourseOrder> wrapper = new EntityWrapper<>();
        wrapper.eq("order_no",orderNo);
        return selectOne(wrapper);
    }

    /**
     * 新增课程订单和订单快照
     * @param courseOrder
     */
    @Override
    @Transactional
    public void saveCourseOrderAndCourseOrderItem(CourseOrder courseOrder) {
        CourseOrder selectCourseOrder = selectByOrderNo(courseOrder.getOrderNo());
        if (selectCourseOrder == null) {
            insert(courseOrder);
            courseOrder.getCourseOrderItems().forEach(e -> {
                e.setOrderId(courseOrder.getId());
                courseOrderItemService.insert(e);
            });

        }
    }

    /**
     * 提交课程秒杀订单
     * @param killCourseOrderDto
     * @return
     */
    @Override
    public String killPlaceOrder(KillCourseOrderDto killCourseOrderDto) {
        // 1.JSR303参数校验
        String orderNo = killCourseOrderDto.getOrderNo();
        Integer payType = killCourseOrderDto.getPayType();
        String token = killCourseOrderDto.getToken();
        // 2.根据预创订单号从redis中查询预创订单
        Object redisKillPreOrderDto = redisTemplate.opsForValue().get(orderNo);
        AssertUtil.isNotNull(redisKillPreOrderDto,GlobalExceptionCode.KILL_ORDER_NO_ERROR);
        KillPreOrderDto killPreOrderDto = JSON.parseObject(JSON.toJSONString(redisKillPreOrderDto), KillPreOrderDto.class);
        BigDecimal killPrice = killPreOrderDto.getKillPrice();
        Long courseId = killPreOrderDto.getCourseId();
        // 3.校验token是否存在，是否正确
        Long loginId = 4L;
        String key = String.format(RedisConstants.COURSE_ORDER_KEY, courseId, loginId);
        Object redisToken = redisTemplate.opsForValue().get(key);
        AssertUtil.isNotNull(redisToken, GlobalExceptionCode.ORDER_TOKEN_NON_EXISTENT_ERROR);
        AssertUtil.isEquals(token,redisToken.toString(),GlobalExceptionCode.ORDER_TOKEN__ERROR);
        // 4.保存课程订单信息t_course_order
        CourseOrder courseOrder = new CourseOrder();
        courseOrder.setCreateTime(new Date());
        courseOrder.setOrderNo(orderNo);
        courseOrder.setPayType(payType);
        courseOrder.setStatusOrder(CourseOrder.WAIT_PAY_STATUS);
        courseOrder.setUserId(loginId);
        courseOrder.setTotalAmount(killPrice);
        courseOrder.setVersion(0);
        courseOrder.setTotalCount(1);
        String title = "购买课程 ["+killPreOrderDto.getCourseName()+"] , 支付[" + killPrice + "] 元";
        courseOrder.setTitle(title);
        // 5.保存课程订单快照系信息t_course_order_item
        CourseOrderItem courseOrderItem = new CourseOrderItem();
        courseOrderItem.setAmount(killPrice);
        courseOrderItem.setCount(1);
        courseOrderItem.setCourseId(killPreOrderDto.getCourseId());
        courseOrderItem.setCreateTime(new Date());
        courseOrderItem.setCoursePic(killPreOrderDto.getCoursePic());
        courseOrderItem.setCourseName(killPreOrderDto.getCourseName());
        courseOrderItem.setOrderNo(orderNo);
        courseOrderItem.setVersion(0);
        courseOrder.getCourseOrderItems().add(courseOrderItem);
        // 7.封装消息message，发送事务消息
        PayDto payDto = new PayDto();
        BeanUtils.copyProperties(courseOrder,payDto);
        PayExtParamDto payExtParamDto = new PayExtParamDto();
        payExtParamDto.setCourseIds(killPreOrderDto.getCourseId().toString());
        payExtParamDto.setUserId(loginId);
        payExtParamDto.setOrderNo(orderNo);
        payDto.setExtParams(JSON.toJSONString(payExtParamDto));
        Message<PayDto> message = MessageBuilder.withPayload(payDto).build();
        TransactionSendResult sendResult = rocketMQTemplate.sendMessageInTransaction("tx-course-order-producer-group",
                "tx-course-order-topic:tx-course-order-tag",
                message,
                JSON.toJSONString(courseOrder));
        boolean sendState = sendResult.getSendStatus() == SendStatus.SEND_OK;
        AssertUtil.isTrue(sendState,GlobalExceptionCode.SEND_TX_MESSAGE_ERROR);
        // 8.删除防重Token
        redisTemplate.delete(key);
        try {
            // 9.发送延迟事务消息
            Message<String> delayMessage = MessageBuilder.withPayload(courseOrder.getOrderNo()).build();
            SendResult syncSendResult = rocketMQTemplate.syncSend("order-timeout-topic:order-timeout-tag",
                    delayMessage,
                    3000, 4);
            if (syncSendResult.getSendStatus() != SendStatus.SEND_OK) {
                log.info("课程订单发送延迟消息失败！");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return orderNo;
    }
}
